#include "scene.h"

#include <cstdio>

////////////////
//  Viewport  //
////////////////

Viewport::Viewport(){
    this->left = 0;
    this->top = 0;
    this->width = 1;
    this->height = 1;
}

Viewport::Viewport(int _left, int _top, int _width, int _height){
    this->setViewport(_left, _top, _width, _height);
}

Viewport::~Viewport(){

}

void Viewport::setViewport(int _left, int _top, int _width, int _height){
    this->left = _left;
    this->top = _top;
    this->width = _width;
    this->height = _height;
}

//////////////
//  Window  //
//////////////

Window::Window(){
    this->left = 0;
    this->right = 0;
    this->bottom = 1;
    this->top = 1;
}

Window::Window(float _left, float _right, float _bottom, float _top){
    this->setWindow(_left, _right, _bottom, _top);
}

Window::~Window(){

}

void Window::setWindow(float _left, float _right, float _bottom, float _top){
    this->left = _left;
    this->right = _right;
    this->bottom = _bottom;
    this->top = _top;
}

////////////////
//  Selected  //
////////////////

Selected::Selected(){

}

Selected::~Selected(){

}

void Selected::show(){
    printf("%i   %i   %i   %i\n", count, zmin, zmax, id);
}

/////////////
//  Scene  //
/////////////

void Scene::setViewport(int _left, int _top, int _width, int _height){
    this->viewport.setViewport(_left, _top, _width, _height);
}

Viewport& Scene::getViewport(){
    return this->viewport;
}

void Scene::setWindow(float _left, float _right, float _bottom, float _top){
    this->window.setWindow(_left, _right, _bottom, _top);
}

Window& Scene::getWindow(){
    return this->window;
}    


void Scene::setRotateX(float rx){
    this->rotate_x = rx;
}

float Scene::getRotateX(){
    return this->rotate_x;
}

void Scene::setRotateY(float ry){
    this->rotate_y = ry;
}

float Scene::getRotateY(){
    return this->rotate_y;
}

void Scene::setRotateZ(float rz){
    this->rotate_z = rz;
}

float Scene::getRotateZ(){
    return this->rotate_z;
}      

void Scene::setTranslateX(float tx){
    this->translate_x = tx;
}

float Scene::getTranslateX(){
    return this->translate_x;
}

void Scene::setTranslateY(float ty){
    this->translate_y = ty;
}

float Scene::getTranslateY(){
    return this->translate_y;
}

void Scene::setTranslateZ(float tz){
    this->translate_z = tz;
}

float Scene::getTranslateZ(){
    return this->translate_z;
}  

void Scene::setScaleX(float sx){
    this->scale_x = sx;
}

float Scene::setScaleX(){
    return this->scale_x;
}

void Scene::setScaleY(float sy){
    this->scale_y = sy;
}

float Scene::setScaleY(){
    return this->scale_y;
}

void Scene::setScaleZ(float sz){
    this->scale_z = sz;
}

float Scene::setScaleZ(){
    return this->scale_z;
}                    

void Scene::rotate(float angle, Point direction){
    glRotatef(angle, direction.getX(), direction.getY(), direction.getZ());
}

Point Scene::getScenePoint(Cursor &cursor){
    GLdouble x, y, z;
    int viewportMatrix[4];
    double modelview[16];
    double projection[16];
    float p[1];    
    
    
    glGetIntegerv(GL_VIEWPORT, viewportMatrix);
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    
    glReadPixels(cursor.getX(), viewport.height - cursor.getY(), 
                 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &p);
    
    gluUnProject(cursor.getX(), viewport.height - cursor.getY(), p[0], 
                 modelview, projection, viewportMatrix, &x, &y, &z);
                
    return Point(x,y,z);
}        

vector<Selected> Scene::parseSelectedBuffer(GLuint selectBuff[SELECT_BUFFER_LENGTH]){
    vector<Selected> selected;
    for (int i = 0; i < SELECT_BUFFER_LENGTH; i+=4){
        if (selectBuff[i] == 1){
            Selected s;
            s.count = selectBuff[i];
            s.zmin = selectBuff[i+1];
            s.zmax = selectBuff[i+2];
            s.id = selectBuff[i+3];
            selected.push_back(s);
        }
        else{
            break;
        }
    }            
    return selected;
}

vector<Selected> Scene::selectElements(int xPos, int yPos){
    GLfloat fAspect;
    GLuint selectBuff[SELECT_BUFFER_LENGTH];
    GLint hits, viewportMatrix[4];

    glSelectBuffer(SELECT_BUFFER_LENGTH, selectBuff);
    glGetIntegerv(GL_VIEWPORT, viewportMatrix);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();

    glRenderMode(GL_SELECT);
    glLoadIdentity();
    gluPickMatrix(xPos, viewportMatrix[3] - yPos + viewportMatrix[1], 2,2, viewportMatrix);

    fAspect = (float)viewportMatrix[2] / (float)viewportMatrix[3];
    glOrtho(window.left, window.right, window.bottom, window.top, -10.0, 10.0);	
    gluPerspective(20.0 , fAspect, -10.0, 30.0);
    gluLookAt(0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0);    

    this->setUp();
    this->drawEnviroment();
    this->drawObjects();

    hits = glRenderMode(GL_RENDER);
    
    vector<Selected> selected = parseSelectedBuffer(selectBuff);

    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

    glMatrixMode(GL_MODELVIEW);
    
    return selected;
}        

void Scene::setPlane(Plane _plane){
    this->plane = _plane;
}

void Scene::enablePlane(){
    this->showPlane = true;
}

void Scene::disablePlane(){
    this->showPlane = false;
}        

void Scene::drawEnviroment(){
    ground.draw();
    if (this->showPlane){
        plane.draw();
    }
}

void Scene::addObject(Object object, int objId){
    objects[objId] = object;
}

void Scene::setObjects(map<int, Object> objs){
    this->objects = objs;
}

map<int, Object>& Scene::getObjects(){
    return this->objects;
}          

void Scene::drawObjects(){
    for (map<int, Object>::iterator it = objects.begin(); 
                                    it != objects.end(); it++){
        it->second.draw(true, true, true);
    }        
}

void Scene::addSphere(Sphere s){
    spheres.push_back(s);
}

void Scene::drawSpheres(){
    for (int i = 0; i < spheres.size(); i++){
        spheres[i].draw();
    }
}


void Scene::addSegment(Segment s){
    segments.push_back(s);
}

void Scene::drawSegments(){
    for (int i = 0; i < segments.size(); i++){
        segments[i].draw();
    }
}
